# Name:    CDataHnd.py
# Date:    Tue Mar 23 23:18:22 CET 2010
# Purpose: main data class of fileRunner application. It loads data from config and data files.
# Def:     CDataHnd()
# Inputs:	

import os
import sys
import string
import fnmatch
import re
import commands
import errno

import time

# my libraries
from CFile import CFile
from CConfigReader import CConfigReader
from CProcMngr import CProcMngr
from CProfile import CProfile
from CExceptionHnd import CExceptionHnd
import myconst
import mmStrMatch

####################################################################################################

class CDataHnd(CExceptionHnd):
	__doExit = None
	__oCfgReader = None
	oDBFile = None
	oHistoryFile = None
	oHistoryFileRO = None
	__oProcMngr = None
	oDbGen = None
	oExcHnd = None

	__entryBoxText = None          # this is set when user inserts the text in the entry box
	__camelCaseExp = None   # this is set when user inserts the text in the entry box

	__searchMode = None # 1st bit - wildcard, 2nd bit - regexp

	__entrySepCnt = None

	__WILDCARD_SEARCH_MODE = 1
	__REGEXP_SEARCH_MODE = 2
	__CAMELCASE_SEARCH_MODE = 4

	__GENERAL_SECTION = 'GENERAL'
	__DB_FILE_OPTION = 'DB_FILE'
	__SEARCH_PATH_OPTION = 'SEARCH_PATH'
	__SYM_LINK_OPTION = 'SYM_LINK'

	__EXTENSIONS_SECTION = 'EXTENSIONS'

	__SEARCH_EXPRESSION_SECTION = 'SEARCH_EXPRESSION'
	__WILDCARD_OPTION = 'WILDCARD'
	__REGEXP_OPTION = 'REGEXP'
	__CAMELCASE_OPTION = 'CAMELCASE'

#---------------------------------------------------------------------------------------------------

	def __init__(self):
		self.__doExit = False
		self.__searchMode = 0
		self.__entrySepCnt = myconst.ENTRY_COMPLETION_SEPARATOR_CNT
		self.__oProfile = CProfile(myconst.CFG_DIR)
		self.__oCfgReader = CConfigReader(myconst.CFG_FILE)
		self.__oProcMngr = CProcMngr()
		
#---------------------------------------------------------------------------------------------------
	
	def load(self, loadDbFile = True):
		self.__oProfile.createProfile()
		self.__oProfile.addFile(os.path.basename(myconst.CFG_FILE))

		self.__oCfgReader.load()

		dbFilename = self.getDbFilename()
		# it is False while regenerating DB
		if loadDbFile is True:
			try:
				self.oDBFile = CFile(dbFilename, 'r')
			except IOError, e:
				#raise CDataHndException(e), IOError(e)#CDataHndException(CDataHndException.DB_FILE_EXC)
				if e.errno == errno.ENOENT:
					raise CDataHndDBFileNotExistExc(e)
				else:
					raise IOError(e)

		self.oHistoryFile = CFile(myconst.HISTORY_FILE, 'a+')
		self.oHistoryFileRO = CFile(myconst.HISTORY_FILE, 'r')
		self.setSearchMode()

#---------------------------------------------------------------------------------------------------

	def getErrno(self):
		return self.__errno

#---------------------------------------------------------------------------------------------------

	def setErrno(self, val):
		self.__errno = val

#---------------------------------------------------------------------------------------------------

	def getDbFilename(self):
		dbFile = self.__oCfgReader.getValue(self.__GENERAL_SECTION, self.__DB_FILE_OPTION)
		return myconst.CFG_DIR + '/' + dbFile

#---------------------------------------------------------------------------------------------------

	def getProcMngr(self):
		return self.__oProcMngr

#---------------------------------------------------------------------------------------------------

	def getSearchMode(self):
		return self.__searchMode

#---------------------------------------------------------------------------------------------------

	def setSearchMode(self):
		wildcard = str(self.__oCfgReader.getValue(self.__SEARCH_EXPRESSION_SECTION, self.__WILDCARD_OPTION))
		if wildcard in '1' or wildcard in 'Yes' or wildcard in 'True':
			self.__searchMode = 0 | self.__WILDCARD_SEARCH_MODE # set 1st bit

		regexp = str(self.__oCfgReader.getValue(self.__SEARCH_EXPRESSION_SECTION, self.__REGEXP_OPTION))
		if regexp in '1' or regexp in 'Yes' or regexp in 'True':
			self.__searchMode = self.__searchMode | self.__REGEXP_SEARCH_MODE # set 2nd bit

		camel = str(self.__oCfgReader.getValue(self.__SEARCH_EXPRESSION_SECTION, self.__CAMELCASE_OPTION))
		if camel in '1' or camel in 'Yes' or camel in 'True':
			self.__searchMode = self.__searchMode | self.__CAMELCASE_SEARCH_MODE # set 3rd bit

#---------------------------------------------------------------------------------------------------

	def getSearchPath(self):
		path = self.__oCfgReader.getValue(self.__GENERAL_SECTION, self.__SEARCH_PATH_OPTION)
		path = path.replace('\n', ' ')
		path = path.replace(':', ' ')

		self.__checkSearchPathsExist(path)

		return path

#---------------------------------------------------------------------------------------------------

	def __checkSearchPathsExist(self, paths):
		for path in paths.split(' '):
			if os.path.exists(path) is False:
				raise IOError(myconst.MSG_SEARCH_PATH_NOT_EXIST)

#---------------------------------------------------------------------------------------------------

	def getSymLink(self):
		return self.__oCfgReader.getValue(self.__GENERAL_SECTION, self.__SYM_LINK_OPTION)

#---------------------------------------------------------------------------------------------------

	def getExtCmd(self):
		return self.__oCfgReader.getSettings(self.__EXTENSIONS_SECTION)

#---------------------------------------------------------------------------------------------------

	def getExt(self):
		ext = []
		for i in self.getExtCmd():
			ext.append(i[0])

		return ext

#---------------------------------------------------------------------------------------------------

	# it is called when entry box text is changed
	def setEntryBoxText(self, entryText):
		self.__entryBoxText = entryText

#---------------------------------------------------------------------------------------------------

	# it is called when entry box text is changed
	def setCamelCaseExpression(self, entryText):
		self.__camelCaseExp = self.__camelCaseToRegExp(entryText)

#---------------------------------------------------------------------------------------------------

	# it is called when entry box text is changed
	def setModel(self, model):
		self.__model = model

#---------------------------------------------------------------------------------------------------

	def match_func_C(self, completion, key, iter, oEntry):
		result = False
		text = self.__model.get_value(iter, 0)

		if self.__entrySepCnt > 0:
			if self.__isEntrySeparator(text) is True:
				return True

		# Wildcard
		if self.__searchMode & self.__WILDCARD_SEARCH_MODE:
			if mmStrMatch.wildcard(self.__entryBoxText, text):
				result = True

		# Regexp		
		if self.__searchMode & self.__REGEXP_SEARCH_MODE:
			try:
				if mmStrMatch.regExp(self.__entryBoxText, text):
					result = True
			except re.error:
				# TODO: raise exception
				if result is not True:
					result = False

		# CamelCase
		if self.__searchMode & self.__CAMELCASE_SEARCH_MODE:
			if self.__camelCaseExp is not None:
				if mmStrMatch.camelCase(self.__camelCaseExp, text):
					result = True

		return result

#---------------------------------------------------------------------------------------------------

	def match_func_Py(self, completion, key, iter, oEntry):
		result = False
		model = completion.get_model()
		text = model.get_value(iter, 0)

		if self.__isEntrySeparator(text) is True:
			return True

		# Wildcard
		if self.__searchMode & self.__WILDCARD_SEARCH_MODE:
			if fnmatch.fnmatch(text, self.__entryBoxText):
				result = True

		# Regexp		
		if self.__searchMode & self.__REGEXP_SEARCH_MODE:
			try:
				if re.search(self.__entryBoxText, text):
					result = True
			except re.error:
				# TODO: raise exception
				if result is not True:
					result = False

		## CamelCase
		if self.__searchMode & self.__CAMELCASE_SEARCH_MODE:
			if self.__camelCaseExp is not None:
				if re.search(self.__camelCaseExp, text):
					result = True

		return result

#---------------------------------------------------------------------------------------------------

	def __isEntrySeparator(self, text):
		if myconst.ENTRY_COMPLETION_HISTORY_SEP in text:
			self.__entrySepCnt = self.__entrySepCnt - 1
			return True

		if myconst.ENTRY_COMPLETION_ALL_SEP in text:
			self.__entrySepCnt = self.__entrySepCnt - 1
			return True

		return False

#---------------------------------------------------------------------------------------------------

	def __camelCaseToRegExp(self, camelStr):
		regExpStr = ''
		for index in range(len(camelStr)):
			if camelStr[index] not in string.uppercase:
				return None
			regExpStr += camelStr[index] + '[a-z]*'

		return regExpStr

#---------------------------------------------------------------------------------------------------

	def getExtFromFilename(self, filename):
		baseFilename = os.path.basename(filename)
		filenameParts = baseFilename.split('.')
		if len(filenameParts) > 0:
			ext = filenameParts[len(filenameParts) - 1]
			return ext
		else:
			return None

#---------------------------------------------------------------------------------------------------

	def runFile(self, file):
		''' Return True if any file was selected from the entry completion box
		''  Return False if the separator entry was selected
		'''

		cmd = None

		if self.__isEntrySeparator(file) is True:
			return False

		ext = self.getExtFromFilename(file)
		if ext is not None:
			cmd = self.__oCfgReader.getValue(self.__EXTENSIONS_SECTION, ext)

		# TODO: Message - cannot 
		if cmd is not None:
			self.__oProcMngr.setCmd(cmd + ' ' + file)
			self.__oProcMngr.createProc()
			self.__addFileToHistory(file)

		return True

#---------------------------------------------------------------------------------------------------

	def __addFileToHistory(self, file):
		if self.__isFileInHistory(file) is False:
			self.oHistoryFile.getFileDesc().write(file + '\n')
			self.oHistoryFile.sync()

#---------------------------------------------------------------------------------------------------

	def __isFileInHistory(self, file):
		self.oHistoryFile.begin()
		result = False

		for line in self.oHistoryFile.getFileDesc():
			if line.replace('\n', '') == file:
				result = True

		return result

#---------------------------------------------------------------------------------------------------

	# prepare function before calling match_func
	def prepareBeforeMatch(self, entryText):
		self.setEntryBoxText(entryText)
		self.setCamelCaseExpression(entryText)
		self.__entrySepCnt = myconst.ENTRY_COMPLETION_SEPARATOR_CNT

####################################################################################################

####################################################################################################
# CLASS EXCEPTIONS
####################################################################################################

class CDataHndDBFileNotExistExc(Exception):
	pass
	# TODO: implement CEnum own class
	#def __init__(self, e):
		#super(CDataHndException, self).__init__(e)
	#	self.errno = e.errno
# enum for exception type
#	CFG_FILE_EXC, DB_FILE_EXC = range(2)
#
#	__type = None
#
#	def __init__(self, type):
#		self.__type= type
#
#	def getType(self):
#		return self.__type
#
####################################################################################################
